package net.pulseproject.commons.midi.collection;

import java.util.Collection;

import javax.sound.midi.MidiDevice;

import net.pulseproject.commons.midi.entity.DeviceKind;
import net.pulseproject.commons.midi.entity.DeviceState;
import net.pulseproject.commons.misc.collection.Filter;
import net.pulseproject.commons.misc.collection.Matcher;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class MidiDeviceFilter extends Filter<MidiDevice> {

	private static final Log LOG = LogFactory.getLog(MidiDeviceFilter.class);

	public final MidiDevice searchMidiDevice(final Collection<MidiDevice> allDevices,
			final String portName, final DeviceKind kind) {
		LOG.debug("getMidiDevice(allDevices, portName=[" + portName + "], kind=" + kind + ")");
		if(allDevices == null) { throw new IllegalArgumentException("allDevices == null"); }
		if(portName == null) { throw new IllegalArgumentException("portName == null"); }
		if(kind == null) { throw new IllegalArgumentException("kind == null"); }
		
		final Collection<MidiDevice> filtered = this.filter(allDevices, new DeviceByKindMatcher(portName, kind));
		
		if(filtered.isEmpty() == true) {
			throw new RuntimeException("Could not find MidiDevice by port name [" + portName + "] and " +
					"kind [" + kind + "]!");
		}
		
		if(filtered.size() != 1) {
			throw new RuntimeException("Multiple MidiDevices (" + filtered.size() + ") found by " +
					"port name [" + portName + "] and kind of [" + kind + "]!");
		}
		
		return filtered.iterator().next();
	}
	
	
	private static class DeviceByKindMatcher implements Matcher<MidiDevice> {
		private final String portName;
		private final DeviceKind kind;
		DeviceByKindMatcher(final String portName, final DeviceKind kind) {
			this.portName = portName;
			this.kind = kind;
		}
		@Override
		public boolean matches(final MidiDevice midiDevice) {
			if(this.portName.equals(midiDevice.getDeviceInfo().getName()) == false) {
				return false;
			}
			
			final DeviceState state = DeviceState.byMaxReceiversTransmitters(
					midiDevice.getMaxReceivers(), midiDevice.getMaxTransmitters());
			return this.kind.stateIsApplicable(state);
		}
	}
}
